const isObject = val => val !== null && typeof val === 'object'
const convert = target => isObject(target) ? reactive(target) : target
const hasKey = target => Object.prototype.hasOwnProperty.call(target, key)

export function reactive(target) {
    if (!isObject(target)) return

    const handler = {
        get(target, key, receiver) {
            track(target, key)
            return convert(Reflect.get(target, key, receiver))
        },
        set(target, key, val, receiver) {
            let value = Reflect.get(target, key, receiver)
            if (val !== value) {
                let res = Reflect.set(target, key, val, receiver)
                trigger(target, key)
                return res
            }
            return true
        },
        deleteProperty(target, key) {
            const res = Reflect.deleteProperty(target, key)
            if (res && hasKey(target, key)) {
                trigger()
            }
            return res
        }
    }
    return new Proxy(target, handler)
}

let activeEffect = null
export function effect(callback) {
    debugger
    activeEffect = callback
    callback()
    activeEffect = null
}

let targetMap = new WeakMap()

function track(target, key) {
    if (!activeEffect) {
        return
    }
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    dep.add(activeEffect)
}

function trigger(target, key) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        return
    }
    let deps = depsMap.get(key)
    if (!deps) {
        return
    }
    deps.forEach(effect => {
        effect()
    })
}
// ref的作用是把传入的参数转换成响应式对象返回
// 重新赋值的对象也是响应式的
export function ref(raw) {
    // 如果是ref创建的对象就返回
    if (isObject(raw) && raw.__v_isRef) return

    let value = convert(raw)
    const r = {
        __v_isRef: true,
        get value() {
            debugger
            track(r, 'value')
            return value
        },
        set value(val) {
            if (val !== value) {
                value = convert(val)
                trigger(r, 'value')
            }
        }
    }
    return r
}

export function toRefs(proxy) {
    const ret = proxy instanceof Array ? new Array(proxy.length) : {}
    for (const k in proxy) {
        ret[k] = toProxyRef(k, proxy)
    }
    return ret
}

function toProxyRef(key, proxy) {
    const r = {
        __v_isRef: true,
        get value() {
            return proxy[key]
        },
        set value(val) {
            proxy[key] = val
        }
    }
    return r
}

export function computed(getter) {
    const res = ref()
    effect(() => {
        res.value = getter()
    })
    return res
}